:- module(queues).
:- export queue_to_list / 2.
:- export list_to_queue / 2.
:- export empty_queue / 1.
:- export length_queue / 2.
:- export serve_queue / 3.
:- export head_queue / 2.
:- export list_jump_queue / 3.
:- export jump_queue / 3.
:- export list_join_queue / 3.
:- export join_queue / 3.
:- export make_queue / 1.
:- comment(summary, "define queue operations").
:- comment(author, "R.A.O'Keefe").
:- comment(date, "Friday November 18th, 1983, 8:09:31 pm").
:- comment(desc, html("<P>\n    In this package, a queue is represented as a term Front-Back,  where\n    Front  is  a list and Back is a tail of that list, and is normally a\n    variable.  join_queue will only work when the Back  is  a  variable,\n    the  other routines will accept any tail.  The elements of the queue\n    are the list difference, that is, all the elements starting at Front\n    and stopping at Back.  Examples:\n<PRE>\n\t[a,b,c,d,e|Z]-Z\t    has elements a,b,c,d,e\n\t[a,b,c,d,e]-[d,e]   has elements a,b,c\n\tZ-Z\t\t    has no elements\n\t[1,2,3]-[1,2,3]\t    has no elements\n</PRE>\n")).
:- comment(empty_queue / 1, [summary : "tests whether the queue is empty", template : "empty_queue(Queue)", desc : html("\n    tests whether the queue is empty.  If the back of a queue were\n    guaranteed to be a variable, we could have\n<PRE>\n \tempty_queue(Front-Back) :- var(Front).\n</PRE>\n    but I don't see why you shouldn't be able to treat difference\n    lists as queues if you want to.\n    ")]).
:- comment(head_queue / 2, [summary : "unifies Head with the first element of the queue", template : "head_queue(Queue, Head)", desc : html("\n    unifies Head with the first element of the queue.  The tricky part\n    is that we might be at the end of a queue: Back-Back, with Back a\n    variable, and in that case this predicate should not succeed, as we\n    don't know what that element is or whether it exists yet.\n    ")]).
:- comment(join_queue / 3, [summary : "adds the new element at the end of the queue", template : "join_queue(Element, OldQueue, NewQueue)", desc : html("\n    adds the new element at the end of the queue.  The old queue is\n    side-effected, so you *can't* do\n<PRE>\n \tjoin_queue(1, OldQ, NewQ1),\n \tjoin_queue(2, OldQ, NewQ2).\n</PRE>\n    There isn't any easy way of doing that, sensible though it might\n    be.  You *can* do\n<PRE>\n \tjoin_queue(1, OldQ, MidQ),\n \tjoin_queue(2, MidQ, NewQ).\n</PRE>\n    "), see_also : [list_join_queue / 3]]).
:- comment(jump_queue / 3, [summary : "adds the new element at the front of the list", template : "jump_queue(Element, OldQueue, NewQueue)", desc : html("\n    adds the new element at the front of the list.  Unlike join_queue,\n<PRE>\n \tjump_queue(1, OldQ, NewQ1),\n \tjump_queue(2, OldQ, NewQ2)\n</PRE>\n    *does* work, though if you add things at the end of NewQ1 they\n    will also show up in NewQ2.  Note that\n<PRE>\n \tjump_queue(1, OldQ, MidQ),\n \tjump_queue(2, MidQ, NewQ)\n</PRE>\n    makes NewQ start 2, 1, ...\n    ")]).
:- comment(length_queue / 2, [summary : "counts the number of elements currently in the queue", template : "length_queue(Queue, Length)", desc : html("\n    counts the number of elements currently in the queue.  Note that\n    we have to be careful in checking for the end of the list, we\n    can't test for [] the way length(List) does.\n    ")]).
:- comment(list_join_queue / 3, [summary : "adds the new elements at the end of the queue", template : "list_join_queue(List, OldQueue, NewQueue)", desc : html("\n    adds the new elements at the end of the queue.  The elements are\n    added in the same order that they appear in the list, e.g.\n<PRE>\n    list_join_queue([y,z], [a,b,c|M]-M, [a,b,c,y,z|N]-N).\n</PRE>\n    ")]).
:- comment(list_jump_queue / 3, [summary : "adds all the elements of List at the front of the queue", template : "list_jump_queue(List, OldQueue, NewQueue)", desc : html("\n    adds all the elements of List at the front of the queue.  There  are\n    two  ways  we might do this.  We could add all the elements one at a\n    time, so that they would appear at the beginning of the queue in the\n    opposite order to the order they had in the list, or  we  could  add\n    them in one lump, so that they have the same order in the  queue  as\n    in  the  list.   As you can easily add the elements one at a time if\n    that is what you want, I have chosen the latter.\n    ")]).
:- comment(list_to_queue / 2, [summary : "creates a new queue with the same elements as List", template : "list_to_queue(List, Queue)"]).
:- comment(make_queue / 1, [summary : "creates a new empty queue", template : "make_queue(Queue)", desc : html("\n    creates a new empty queue.  It will also match empty queues, but\n    because Prolog doesn't do the occurs check, it will also match\n    other queues, creating circular lists.  So this should ONLY be\n    used to make new queues.\n    ")]).
:- comment(queue_to_list / 2, [summary : "creates a new list with the same elements as Queue", template : "queue_to_list(Queue, List)"]).
:- comment(serve_queue / 3, [summary : "removes the first element of the queue for service", template : "serve_queue(OldQueue, Head, NewQueue)"]).
